package com.ibeeking.found.upms.b.rest.service.impl;

import com.ibeeking.found.upms.b.rest.service.ITenantRoleService;
import com.ibeeking.found.upms.b.rest.service.ITenantUserRoleService;
import com.ibeeking.found.common.base.BaseDO;
import com.ibeeking.found.common.constants.RoleConstant;
import com.ibeeking.found.common.enums.PublishStatusEnum;
import com.ibeeking.found.common.utils.UserUtils;
import com.ibeeking.found.upms.service.common.bo.TenantRoleBO;
import com.ibeeking.found.upms.service.common.bo.TenantUserRoleBO;
import com.ibeeking.found.upms.service.common.constants.RedisKeyConstant;
import com.ibeeking.found.upms.service.common.dos.TenantRoleDO;
import com.ibeeking.found.upms.service.common.param.TenantRoleParam;
import com.ibeeking.found.upms.service.common.query.TenantRolePageQuery;
import com.ibeeking.found.upms.service.common.query.TenantRoleQuery;
import com.ibeeking.found.upms.service.common.vo.RoleSelectVO;
import com.ibeeking.found.upms.service.common.vo.TenantRolePageVO;
import com.ibeeking.found.upms.service.common.vo.TenantRoleVO;
import com.ibeeking.found.upms.service.mapper.TenantRoleMapper;
import com.ibeeking.nematos.core.utils.SpringBeanUtil;
import com.ibeeking.nematos.exception.BusinessException;
import com.ibeeking.nematos.redis.annotation.CacheEvict;
import com.ibeeking.nematos.redis.annotation.CacheQuery;
import com.ibeeking.nematos.utils.bean.BeanUtil;
import com.ibeeking.nematos.utils.data.Collections3;
import com.ibeeking.nematos.utils.data.StringUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @ClassName GlobalRoleServiceImpl
 * @Description
 * @Author ibeeking
 * @Date 2020-12-02 15:29
 **/
@Service
public class TenantRoleServiceImpl extends ServiceImpl<TenantRoleMapper, TenantRoleDO> implements ITenantRoleService {

    @Resource
    private TenantRoleMapper tenantRoleMapper;
    @Resource
    private ITenantUserRoleService userRoleServiceImpl;

    @Override
    public Page<TenantRolePageVO> pageList(TenantRolePageQuery rolePageQuery) {
        LambdaQueryWrapper<TenantRoleDO> queryWrapper = new LambdaQueryWrapper<>(TenantRoleDO.class);
        queryWrapper.orderByAsc(TenantRoleDO::getSort).orderByDesc(BaseDO::getCreateTime);

        if (null != rolePageQuery.getPublishStatus()) {
            queryWrapper.eq(TenantRoleDO::getPublishStatus, rolePageQuery.getPublishStatus());
        }

        if (StringUtils.isNotBlank(rolePageQuery.getCode())) {
            queryWrapper.like(TenantRoleDO::getCode, rolePageQuery.getCode());
        }

        if (StringUtils.isNotBlank(rolePageQuery.getName())) {
            queryWrapper.like(TenantRoleDO::getName, rolePageQuery.getName());
        }
        Page<TenantRoleDO> page = tenantRoleMapper.selectPage(new Page<>(rolePageQuery.getPageNum(), rolePageQuery.getPageSize()), queryWrapper);
        Page<TenantRolePageVO> result = BeanUtil.convertPage(page, TenantRolePageVO.class);
        if (!Collections3.isEmpty(result.getRecords())) {
            List<Long> roleIds = result.getRecords().parallelStream().map(TenantRolePageVO::getId).collect(Collectors.toList());
            List<TenantUserRoleBO> tenantUserRoleBos = userRoleServiceImpl.queryUserRoleByRoleIds(roleIds);
            Map<Long, List<TenantUserRoleBO>> collect = tenantUserRoleBos.parallelStream().collect(Collectors.groupingBy(TenantUserRoleBO::getRoleId));
            result.getRecords().stream().forEach(tenantRolePageVO -> {
                tenantRolePageVO.setCount(null == collect.get(tenantRolePageVO.getId()) ? 0 : collect.get(tenantRolePageVO.getId()).size());
            });
        }
        return result;
    }

    @Override
    public List<RoleSelectVO> list(TenantRoleQuery query) {
        //查询所有有效角色
        List<TenantRoleDO> roleDos = tenantRoleMapper.selectList(new LambdaQueryWrapper<>(TenantRoleDO.class)
                .eq(TenantRoleDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                .eq(StringUtils.isNotEmpty(query.getCode()), TenantRoleDO::getCode, query.getCode())
                .like(StringUtils.isNotEmpty(query.getName()), TenantRoleDO::getName, query.getName())
                .orderByAsc(TenantRoleDO::getSort)
                .orderByDesc(BaseDO::getCreateTime)
        );
        if (Collections3.isEmpty(roleDos)) {
            return Collections3.emptyList();
        }
        return JsonUtils.jsonToList(roleDos, RoleSelectVO.class);
    }

    @Override
    public Boolean add(TenantRoleParam roleParam) {
        TenantRoleDO roleDO = BeanUtil.convertBean(roleParam, TenantRoleDO.class);
        if (!checkUnique(roleDO)) {
            //throw new BusinessException("当前角色编码或名称已存在");
            throw new BusinessException("当前角色名称已存在");
        }
        roleDO.setCode("");
        roleDO.setCreateBy(UserUtils.getUserId());
        roleDO.setCreateTime(LocalDateTime.now());
        roleDO.setModifyTime(LocalDateTime.now());
        return tenantRoleMapper.insert(roleDO) > 0;
    }

    @CacheEvict(cacheName = RedisKeyConstant.MODULE_B + RedisKeyConstant.ROLE, key = "#roleParam.id")
    @Override
    public Boolean modify(TenantRoleParam roleParam) {
        TenantRoleDO roleDO = BeanUtil.convertBean(roleParam, TenantRoleDO.class);
        if (!checkUnique(roleDO)) {
            //throw new BusinessException("当前角色编码或名称已存在");
            throw new BusinessException("当前角色名称已存在");
        }
        roleDO.setCode(null);
        roleDO.setModifyBy(UserUtils.getUserId());
        roleDO.setModifyTime(LocalDateTime.now());
        return tenantRoleMapper.updateById(roleDO) > 0;
    }

    @Override
    public TenantRoleVO view(Long id) {
        TenantRoleBO roleBO = SpringBeanUtil.getBean(ITenantRoleService.class).queryById(id);
        return BeanUtil.convertBean(roleBO, TenantRoleVO.class);
    }

    @CacheQuery(cacheName = RedisKeyConstant.MODULE_B + RedisKeyConstant.ROLE, key = "#id", expires = RedisKeyConstant.MONTH)
    @Override
    public TenantRoleBO queryById(Long id) {
        TenantRoleDO roleDO = tenantRoleMapper.selectById(id);
        return BeanUtil.convertBean(roleDO, TenantRoleBO.class);
    }


    @CacheEvict(cacheName = RedisKeyConstant.MODULE_B + RedisKeyConstant.ROLE, key = "#id")
    @Override
    public Boolean changeStatus(Long id, Integer status, Long userId) {
        TenantRoleDO roleDO = new TenantRoleDO();
        roleDO.setId(id);
        roleDO.setPublishStatus(status);
        if (id.equals(getBean().selectAdministratorId())) {
            throw new BusinessException("超级管理员不允许禁用");
        }
        roleDO.setModifyBy(userId);
        roleDO.setModifyTime(LocalDateTime.now());
        return tenantRoleMapper.updateById(roleDO) > 0;
    }

    @Override
    public Boolean del(Long id) {
        if (id.equals(getBean().selectAdministratorId())) {
            throw new BusinessException("超级管理员不允许删除");
        }

        if (PublishStatusEnum.ENABLE.getValue().equals(getBean().queryById(id).getPublishStatus())) {
            throw new BusinessException("请先禁用后再删除");
        }
        return tenantRoleMapper.deleteById(id) > 0;
    }

    @Override
    public List<RoleSelectVO> selectRoleList(Long userId) {
        //查询所有有效角色
        List<TenantRoleDO> roleDos = tenantRoleMapper.selectList(new LambdaQueryWrapper<>(TenantRoleDO.class)
                .eq(TenantRoleDO::getPublishStatus, PublishStatusEnum.ENABLE.getValue())
                .orderByAsc(TenantRoleDO::getSort)
                .orderByDesc(BaseDO::getCreateTime)
        );
        if (Collections3.isEmpty(roleDos)) {
            return Collections3.emptyList();
        }
        List<TenantUserRoleBO> tenantUserRoleBos = userRoleServiceImpl.queryUserRoleByUserId(userId);
        Long administratorId = getBean().selectAdministratorId();
        List<Long> roleIds = tenantUserRoleBos.stream().map(TenantUserRoleBO::getRoleId).collect(Collectors.toList());
        boolean administrator = roleIds.contains(administratorId);
        if (administrator) {
            return JsonUtils.jsonToList(roleDos, RoleSelectVO.class);
        } else {
            List<TenantRoleDO> result = roleDos.stream().filter(roleDO -> roleIds.contains(roleDO.getId())).collect(Collectors.toList());
            return JsonUtils.jsonToList(result, RoleSelectVO.class);
        }
    }

    @Override
    public List<String> queryUserIdsByRoleId(Long roleId) {
        List<TenantUserRoleBO> tenantUserRoleBos = userRoleServiceImpl.queryUserRoleByRoleId(roleId);
        return tenantUserRoleBos.parallelStream().map(userRoleBO -> String.valueOf(userRoleBO.getUserId())).distinct().collect(Collectors.toList());
    }

    @Override
    public Long selectAdministratorId() {
        TenantRoleDO roleDO = tenantRoleMapper.selectOne(new LambdaQueryWrapper<>(TenantRoleDO.class).eq(TenantRoleDO::getCode, RoleConstant.ADMINISTRATOR));
        return roleDO.getId();
    }

    private static ITenantRoleService getBean() {
        return SpringBeanUtil.getBean(ITenantRoleService.class);
    }

    private Boolean checkUnique(TenantRoleDO roleDO) {
        Integer count = tenantRoleMapper.selectCount(new LambdaQueryWrapper<>(TenantRoleDO.class)
                .ne(null != roleDO.getId(), TenantRoleDO::getId, roleDO.getId())
                .and(
                        Wrapper -> Wrapper
                                /*.eq(StringUtils.isNotBlank(roleDO.getCode()), TenantRoleDO::getCode, roleDO.getCode())
                                .or()*/
                                .eq(StringUtils.isNotBlank(roleDO.getName()), TenantRoleDO::getName, roleDO.getName())
                )
        );
        return 0 == count;
    }
}
